local function do_parse(userid_rule)
    -- { userid_rule = "xxx", exact_rules = {"unit_b" = {"11111" = true,
    --                                                   "2222" = true}},
    --                        rules = {"unit_a" = {start = 2,end = -1,max = 10,
    --                                             value_min = 0,value_max = 9},
    --                                }}
    local package_rule_key = "unit_userid_rule"
    if userid_rule == "" then
        package.loaded[package_rule_key] = nil
        return nil
    end
    local old_parsed_rule = package.loaded[package_rule_key]
    if userid_rule == nil then return old_parsed_rule end
    userid_rule = string.lower(userid_rule)
    userid_rule = string.gsub(userid_rule, "%s", "")
    if old_parsed_rule and old_parsed_rule.userid_rule == userid_rule then
        --ngx.log(ngx.ERR, "using cached rule")
        return old_parsed_rule
    end
    local parsed_rule = {}
    local rules = {}
    local exact_rules = {}
    local ruled = false
    for rule_str in string.gmatch(userid_rule, "(%S-:%b{})%b{};?") do
        local unit, rstr = string.match(rule_str, "(%S+):%{(%S+)%}$")
        if not unit or not rstr then
            --ngx.log(ngx.ERR, "not unit or not rstr")
            return old_parsed_rule
        end
        if string.sub(rstr, 1, 1) == "(" then
            local pos_mod, max, value_min, value_max = string.match(rstr,
                  "%(userid%%(%d+)%)%%(%d+)in%((%d+)%.%.(%d+)%)")
            if not pos_mod or not max or not value_min or not value_max then
                --ngx.log(ngx.ERR, "not pos_mod or not max")
                return old_parsed_rule
            end
            local str, start = string.gsub(pos_mod, "0", "0")
            if start < 1 then return old_parsed_rule end
            start = start * -1
            rule = {["start"] = start , ["end"] = -1}
            rule.max = tonumber(max)
            rule.value_min = tonumber(value_min)
            rule.value_max = tonumber(value_max)
            if not rule.max or not rule.value_min or not rule.value_max then
                --ngx.log(ngx.ERR, "not rule.max or not rule.value_min")
                return old_parsed_rule
            end
            if rule.value_max < rule.value_min then return old_parsed_rule end
            --if rule.max <= rule.value_max then return old_parsed_rule end
            rules[unit] = rule
        else
            local values = {}
            local exact = false
            for value in string.gmatch(rstr, "%d+") do
                values[value] = true
                exact = true
            end
            if not exact then
                --ngx.log(ngx.ERR, "#values <= 0")
                return old_parsed_rule
            end
            exact_rules[unit] = values
        end
        ruled = true
    end
    if not ruled then return old_parsed_rule end
    parsed_rule.rules = rules
    parsed_rule.exact_rules = exact_rules
    parsed_rule.userid_rule = userid_rule
    package.loaded[package_rule_key] = parsed_rule
    return parsed_rule
end

local function set_ucn(val)
    ngx.header["Set-Cookie"] = "ucn=" .. val .. "; Expires="
                    .. os.date("%a, %d %b %Y %X GMT", os.time() + 315360000)
                    .. "; Path=/;"
    return val
end

local default_unit
if ngx.var.cell_random then
    default_unit = ngx.var.self_cell
else
    default_unit = "center"
end
local userid_rule = ngx.arg[2]
local parsed_rule = do_parse(userid_rule)
if not parsed_rule then
    if userid_rule == "" then return -1 end
    return default_unit
end
local userid = ngx.arg[1]
if userid == nil or userid == "" then
    return default_unit
end
for unit, rule in pairs(parsed_rule.exact_rules) do
    if rule[userid] then
        return set_ucn(unit)
    end
end
for unit, rule in pairs(parsed_rule.rules) do
    local value = tonumber(string.sub(userid, rule["start"],
                                      rule["end"]))
    if not value then return set_ucn(default_unit) end
    value = value % rule.max
    if value >= rule.value_min and value <= rule.value_max then
        return set_ucn(unit)
    end
end
return default_unit
